void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

// TODO(allen): Use giant source files!

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

    
    // TODO(allen): Use really big code files!
    
    
    
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

// deepinfile




internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}

/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Application layer for project codename "4ed"
 *
 */

struct Partition_Data{
	u8 *memory_base;
	u8 *memory_cursor;
	u32 max_size;
};

internal void*
partition_allocate(Partition_Data *data, u32 size){
	void *ret = 0;
	if ((data->memory_cursor - data->memory_base) + size <= data->max_size &&
		size > 0){
		ret = data->memory_cursor;
		data->memory_cursor += size;
	}
	return ret;
}

struct Cursor_Data{
	u32 pos;
	u32 line_number, line_off;
};

struct Panel_Cursor_Data{
	u32 pos;
	u32 x, y;
};

struct Editing_Data{
	u32 size, max_size;
	void *data;
	
	Cursor_Data cursor;
};

struct Editing_Style{
	Glyph_Set *set;
	u32 cursor_color;
	u32 default_color;
	u32 at_cursor_color;
};

struct Editing_Panel{
	i32 x, y;
	i32 w, h;
	u32 tab_width;
	
	Panel_Cursor_Data cursor;
	u32 scroll_y;
	
	Editing_Data *buffer;
	Editing_Style *style;
};

/*
 * Buffer Functions
 */

// TODO(allen):
//
// function(s) for computing cursor_pos from line/off for skip to position
// function(s) for computing line/off from cursor_pos
//
// handle \r \n and \r\n modes better search for RNRN
//
// non-line wrapping rendering function
//
// handle buffer out-of-memory situations
//
// buffer open / save
//

internal bool32
buffer_open(Editing_Data *buffer, c_str filename,
			void *data_space, u32 data_space_size){
	bool32 result = 0;
	
	File_Data file = system_load_file(filename);
	if (file.size < data_space_size){
		result = 1;
		buffer->size = file.size;
		buffer->data = data_space;
		buffer->max_size = data_space_size;
		for (u32 i = 0; i < file.size; ++i){
			((u8*)buffer->data)[i] =
				((u8*)file.data)[i];
		}
		((u8*)buffer->data)[buffer->size] = 0;
	}
	system_free_file(file);
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_pos(Editing_Panel *panel, u32 pos){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (cursor_pos < pos ||
			data[cursor_pos] == '\r')
		   ){

		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			counted_x = 0;
		}
 		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal Panel_Cursor_Data
panel_compute_cursor_from_xy(Editing_Panel *panel, u32 cursor_x, u32 cursor_y){
	Panel_Cursor_Data result = {};
	
	u32 cursor_pos = 0;
	u32 counted_x = 0;
	u32 counted_y = 0;
	u8 *data = (u8*)panel->buffer->data;
	u32 size = panel->buffer->size;
	u32 character_w = panel->style->set->glyphs[' '].advance;
	u32 max_line_length = panel->w / character_w;
	
	// NOTE(allen): RNRN, this is made for rn mode
	while (cursor_pos < size &&
		   (counted_y < cursor_y ||
			counted_x < cursor_x ||
			data[cursor_pos] == '\r')
		   ){
		
		if (data[cursor_pos] == '\r'){
			// DO NOTHING
		}
		else if (data[cursor_pos] == '\n' ||
				 counted_x+1 >= max_line_length){
			
			++counted_y;
			if (counted_y > cursor_y){
				// TODO(allen): error here?
				// return error code? pointer out? struct?
				// just give closest possible? silent in editing_data?
				--counted_y;
				break;
			}
			
			counted_x = 0;
		}
		else if (data[cursor_pos] == '\t'){
			counted_x += panel->tab_width;
		}
		else{
			++counted_x;
		}
		
		++cursor_pos;
	}
	
	result.pos = cursor_pos;
	result.x = counted_x;
	result.y = counted_y;
	
	return result;
}

internal void
panel_cursor_match_to_panel(Editing_Panel *panel){
	panel->buffer->cursor.pos = panel->cursor.pos;
}

internal bool32
buffer_insert(Editing_Data *editing_data, u32 pos, u8 character){
	bool32 result = 0;
	
	if (editing_data->size + 1 < editing_data->max_size){
		result = 1;
		u8 *data = (u8*)editing_data->data;
		for (i32 pos = editing_data->size;
			 pos >= (i32)editing_data->cursor.pos; --pos){
			data[pos+1] = data[pos];
		}
		data[editing_data->cursor.pos] = character;
		++editing_data->size;
	}
	else{
		// TODO(allen): automatically try to fix right here when this
		// happens, or try to avoid this at all costs?
	}
	
	return result;
}

internal void
panel_insert(Editing_Panel *panel, u8 character){
	Editing_Data *editing_data = panel->buffer;
	if (buffer_insert(editing_data, editing_data->cursor.pos, character)){
		++editing_data->cursor.pos;
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_left(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u8 *data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos > 0){
		--editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos > 0){
			--editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal void
panel_move_right(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	u32 size = editing_data->size;
	u8* data = (u8*)editing_data->data;
	
	if (editing_data->cursor.pos < size){
		++editing_data->cursor.pos;
		
		// NOTE(allen): RNRN, this is made for rn mode
		while (data[editing_data->cursor.pos] == '\r' &&
			   editing_data->cursor.pos < size){
			++editing_data->cursor.pos;
		}
	}
	
	panel->cursor =
		panel_compute_cursor_from_pos(panel, editing_data->cursor.pos);
	panel_cursor_match_to_panel(panel);
}

internal bool32
buffer_delete(Editing_Data *editing_data, u32 pos){
	bool32 did_delete = 0;
	if (pos >= 0 && pos < editing_data->size){
		did_delete = 1;
		i32 size = (i32)(--editing_data->size);
		u8 *data = (u8*)editing_data->data;
		for (i32 adjust_pos = (i32)pos; adjust_pos < size; ++adjust_pos){
			data[adjust_pos] = data[adjust_pos+1];
		}
	}
	return did_delete;
}

internal void
panel_backspace(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->cursor.pos > 0 &&
		buffer_delete(editing_data, editing_data->cursor.pos-1)){
		
		panel->cursor =
			panel_compute_cursor_from_pos(panel, editing_data->cursor.pos-1);
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_delete(Editing_Panel *panel){
	Editing_Data *editing_data = panel->buffer;
	if (editing_data->size > 0){
		buffer_delete(editing_data, editing_data->cursor.pos);
	}
}

internal void
panel_move_up(Editing_Panel *panel){
	if (panel->cursor.y > 0){
		Panel_Cursor_Data newpos =
			panel_compute_cursor_from_xy(panel,
										 panel->cursor.x,
										 panel->cursor.y-1);
		
		panel->cursor = newpos;
		panel_cursor_match_to_panel(panel);
	}
}

internal void
panel_move_down(Editing_Panel *panel){
	Panel_Cursor_Data newpos =
		panel_compute_cursor_from_xy(panel,
									 panel->cursor.x,
									 panel->cursor.y+1);
	
	panel->cursor = newpos;
	panel_cursor_match_to_panel(panel);
}

internal void
panel_basic_mode_key_event(Key_Codes *codes,
						   Editing_Panel *panel,
						   Key_Event_Data key_data){
	u16 character = key_data.character;
	if (character != 0){
		panel_insert(panel, (u8)character);
	}else{
		
		if (key_data.keycode == codes->left){
			panel_move_left(panel);
		}		
		else if (key_data.keycode == codes->right){
			panel_move_right(panel);
		}		
		else if (key_data.keycode == codes->back){
			panel_backspace(panel);
		}
		
		else if (key_data.keycode == codes->del){
			panel_delete(panel);
		}		
		else if (key_data.keycode == codes->up){
			panel_move_up(panel);
		}
		else if (key_data.keycode == codes->down){
			panel_move_down(panel);
		}
	}	
}

internal void
panel_draw(Render_Target *target,
		   Editing_Panel *panel,
		   bool32 is_active){

	Editing_Style *style = panel->style;
	Glyph_Set *set = style->set;
	
	i32 character_w = set->glyphs[' '].advance;
	i32 character_h = set->line_skip;
	i32 offset_x = panel->x;
	i32 offset_y = panel->y;
	i32 max_x = panel->w;
	i32 max_y = panel->h;
	i32 max_line_length = max_x / character_w;
	i32 max_lines = max_y / character_h;
	
	AllowLocal(max_line_length);
	AllowLocal(max_lines);
	
	u32 tab_width = panel->tab_width;
	u32 size = panel->buffer->size;
	u8 *data = (u8*)panel->buffer->data;
	
	Panel_Cursor_Data start_cursor;
	start_cursor =
		panel_compute_cursor_from_xy(panel, 0, panel->scroll_y);
	
	u32 start_character = start_cursor.pos;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	for (u32 character_i = start_character;
		 character_i < size && data[character_i];
		 ++character_i){
		
		if (pos_x + character_w > max_x){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		u8 to_render = data[character_i];
		
		if (character_i == panel->cursor.pos){
			if (is_active){
				draw_rectangle(target,
							   offset_x + pos_x,
							   offset_y + pos_y,
							   character_w,
							   set->line_skip,
							   style->cursor_color);
			}
			else{
				draw_rectangle_outline(target,
									   offset_x + pos_x,
									   offset_y + pos_y,
									   character_w,
									   set->line_skip,
									   style->cursor_color);
			}
		}
		
		// NOTE(allen): RNRN, made for rn mode
		if (to_render == '\r'){
			// DO NOTHING
		}
		
		else if (to_render == '\n'){
			pos_x = 0;
			pos_y += set->line_skip;
		}
		
		else if (to_render == '\t'){
			pos_x += character_w*tab_width;
		}
		
		else if (set->glyphs[to_render].data){
			i32 advance = set->glyphs[to_render].advance;
			
			u32 char_color = style->default_color;
			if (character_i == panel->cursor.pos && is_active){
				char_color = style->at_cursor_color;
			}
			
			font_draw_glyph(target, set, to_render,
							offset_x + pos_x, offset_y + pos_y,
							char_color);
			pos_x += advance;
		}
		
		else{
			i32 advance = character_w;
			
			pos_x += advance;
		}
		
		if (pos_y + set->line_skip > max_y){
			break;
		}
	}
}

struct App_Vars{
	Glyph_Set glyph_set;
	Editing_Data buffer, buffer2;
	i32 active_panel;
	i32 prev_width, prev_height;
	
	Editing_Panel panels[2];
	Editing_Style style;

	i32 last_click_x, last_click_y;
};

internal bool32
app_init(Application_Memory *memory){
	
	if (font_init() != 0){
		FatalError("Error initializing fonts");
		return 0;
	}
	
	Partition_Data partition = {};
	partition.memory_base = 
		partition.memory_cursor = (u8*)memory->main_memory;
	partition.max_size = memory->main_memory_size;	
	
	memory->font_memory_size = Kbytes(512);
	memory->font_memory =
		partition_allocate(&partition, memory->font_memory_size);
	Assert(memory->font_memory);
	
	memory->vars_memory_size = sizeof(App_Vars);
	memory->vars_memory =
		partition_allocate(&partition, memory->vars_memory_size);
	Assert(memory->vars_memory);
	
	memory->buffer_memory_size = memory->main_memory_size -
		(memory->vars_memory_size + memory->font_memory_size);
	memory->buffer_memory =
		partition_allocate(&partition, memory->buffer_memory_size);
	Assert(memory->buffer_memory);
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;
	
	// NOTE(allen): font setup
	i32 memory_used = 0;
	if (font_load(&vars->glyph_set,
				  15, memory->font_memory,
				  font_predict_size(15), &memory_used) != 0){
		FatalError("Could not find any fonts");
	}
	
	// NOTE(allen): buffer setup
	u32 buffer_width = Mbytes(1);
	u8 *buffer_memory_cursor = (u8*)memory->buffer_memory;
	
	vars->buffer = {};
	buffer_open(&vars->buffer, "../code/win32_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;
	
	vars->buffer2 = {};
	buffer_open(&vars->buffer2, "long_4ed.cpp",
				buffer_memory_cursor, buffer_width);
	buffer_memory_cursor += buffer_width;

	// NOTE(allen): style and panels setup
	vars->style.set = &vars->glyph_set;
	vars->style.cursor_color = 0xFF00FF00;
	vars->style.default_color = 0xFFFFFFFF;
	vars->style.at_cursor_color = 0xFF222222;
	
	Editing_Panel *panels = vars->panels;
	
	panels[0].tab_width = 4;
	panels[0].cursor = {};
	panels[0].scroll_y = 0;
	panels[0].buffer = &vars->buffer;
	panels[0].style = &vars->style;
	
	panels[1].tab_width = 4;
	panels[1].cursor = {};
	panels[1].scroll_y = 0;
	panels[1].buffer = &vars->buffer2;
	panels[1].style = &vars->style;
	
	return 1;
}

internal u8*
str_from_int(i32 number, u8 *buffer_space, u32 buffer_size){
	
	u8 *result = 0;
	bool32 is_negative = (number < 0);
	if (is_negative){
		number = -number;
	}
	
	if (number == 0){
		buffer_space[buffer_size - 1] = 0;
		buffer_space[buffer_size - 2] = '0';
		result = buffer_space + buffer_size - 2;
	}
	
	else{
		buffer_space[buffer_size - 1] = 0;
		u32 str_write_pos = buffer_size - 1;
		while (number > 0 && str_write_pos > 0){
			--str_write_pos;
			u8 to_write = (u8)(number % 10);
			number /= 10;
			buffer_space[str_write_pos] = '0' + to_write;
		}
		if (is_negative && str_write_pos > 0){
			--str_write_pos;
			buffer_space[str_write_pos] = '-';
		}
		result = buffer_space + str_write_pos;
	}

	return result;
}

internal void
app_step(Key_Codes *codes,
		 Key_Input_Data *input, Mouse_State *mouse,
		 bool32 time_step, Render_Target *target,
		 Application_Memory *memory){
	
	App_Vars *vars = (App_Vars*)memory->vars_memory;

	u32 background_color = 0xFF0C0C0C;
	
	i32 debug_bar_height = vars->glyph_set.line_skip;
	Editing_Panel *panels = vars->panels;

	panels[0].x = 2;
	panels[0].y = debug_bar_height;
	panels[0].w = target->width / 2 - 4;
	panels[0].h = target->height - debug_bar_height;
	
	panels[1].x = panels[0].x + panels[0].w + 5;
	panels[1].y = debug_bar_height;
	panels[1].w = target->width / 2 - 5;
	panels[1].h = target->height - debug_bar_height;
	
	draw_clear(target, background_color);
	
	if (vars->prev_width != target->width ||
		vars->prev_height != target->height){
		
		for (i32 panel_i = 0; panel_i < ArrayCount(vars->panels); ++panel_i){
			panels[panel_i].cursor =
				panel_compute_cursor_from_pos(&panels[panel_i],
											  panels[panel_i].cursor.pos);
		}
	}
	
	if (time_step){
		
		i32 mx = mouse->x;
		i32 my = mouse->y;
		i32 clicked_panel = -1;
		bool32 mouse_press_event = 0;
		
		if (mouse->left_button && !mouse->left_button_prev){
			mouse_press_event = 1;
		}
		
		for (i32 panel_i = 0;
			 panel_i < ArrayCount(vars->panels);
			 ++panel_i){
			
			Glyph_Set *set = &vars->glyph_set;
			
			i32 character_w = set->glyphs[' '].advance;
			i32 character_h = set->line_skip;
			i32 max_line_length = panels[panel_i].w / character_w;
			i32 max_lines = panels[panel_i].h / character_h;
			
			if (mouse_press_event && // NOTE(allen): check if mouse event has occured
				mx >= panels[panel_i].x && // NOTE(allen): check if mouse is inside this panel
				mx < panels[panel_i].w + panels[panel_i].x &&
				my >= panels[panel_i].y &&
				my < panels[panel_i].h + panels[panel_i].y){
				
				if (clicked_panel == -1){
					clicked_panel = panel_i;
					
					i32 grid_x = (mx - panels[panel_i].x) / character_w;
					i32 grid_y = (my - panels[panel_i].y) / character_h;

					vars->last_click_x = grid_x;
					vars->last_click_y = grid_y;
					
					vars->active_panel = panel_i;
					Editing_Panel *active_panel = &panels[panel_i];
					
					if (grid_x >= 0 && grid_x < max_line_length &&
						grid_y >= 0 && grid_y < max_lines){
						
						i32 pos_x = grid_x;
						i32 pos_y = grid_y + active_panel->scroll_y;
						
						active_panel->cursor =
							panel_compute_cursor_from_xy(active_panel,
														 pos_x, pos_y);
						panel_cursor_match_to_panel(active_panel);
						
					}
					
				}
				else{
					// TODO(allen): debug diagnostics, overlapped panels?
				}
			}
		}
		
		Editing_Panel *active_panel = &panels[vars->active_panel];
		
		if (!input->control_keys[CONTROL_KEY_CONTROL] &&
			!input->control_keys[CONTROL_KEY_ALT]){
			for (u32 i = 0; i < input->press_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->presses[i]);
			}
		
			for (u32 i = 0; i < input->hold_count; ++i){
				panel_basic_mode_key_event(codes, active_panel, input->holds[i]);
			}
		}
		
		// TODO(allen): abstract the max_lines/max_line_length computations
		u32 cursor_y = active_panel->cursor.y;
		u32 scroll_y = active_panel->scroll_y;
		u32 character_h = active_panel->style->set->line_skip;
		u32 max_lines = active_panel->h / character_h;
		while (cursor_y >= scroll_y + max_lines){
			scroll_y += Max(1, max_lines / 4);
		}
		
		while (cursor_y < scroll_y){
			scroll_y -= Max(1, max_lines / 4);
		}
		
		active_panel->scroll_y = scroll_y;
	}
	
	panel_draw(target, &panels[0], vars->active_panel == 0);
	
	draw_rectangle(target, target->width / 2, 0, 1, target->height, 0xFFFFFFFF);
	
	panel_draw(target, &panels[1], vars->active_panel == 1);
	
	// NOTE(allen): debug bar
	Glyph_Set *set = &vars->glyph_set;
	
	draw_rectangle(target, 0, 0, target->width, debug_bar_height, 0xFF440000);
	
	u8 spacer_str[] = " -- ";
	u32 char_color = 0xFF00FFFF;
	
	i32 pos_x = 0;
	i32 pos_y = 0;
	
	Editing_Panel *active_panel = &panels[vars->active_panel];
	
	u8 str[16];
	u8 *str_out;
	
	str_out= str_from_int(vars->active_panel, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)active_panel->cursor.y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}

	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_x, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	for (i32 i = 0; spacer_str[i]; ++i){
		font_draw_glyph(target, set, spacer_str[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[spacer_str[i]].advance;
	}
	
	str_out = str_from_int((i32)vars->last_click_y, str, 16);
	for (i32 i = 0; str_out[i]; ++i){
		font_draw_glyph(target, set, str_out[i], pos_x, pos_y, char_color);
		pos_x += set->glyphs[str_out[i]].advance;
	}
	
	vars->prev_width = target->width;
	vars->prev_height = target->height;
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������